package com.vcc.web.controller;


import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.mybatisflex.core.paginate.Page;
import com.vcc.core.annotations.KycReject;
import com.vcc.core.annotations.ROReject;
import com.vcc.core.annotations.RepeatSubmit;
import com.vcc.core.annotations.RestPack;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.model.file.rechargeLog.RechargeLogFilePojo;
import com.vcc.core.util.DateUtil;
import com.vcc.core.util.FileUtil;
import com.vcc.core.util.IdWorker;
import com.vcc.core.util.RegexUtil;
import com.vcc.data.entity.*;
import com.vcc.service.client.dto.rechargeLog.CreateOrderDto;
import com.vcc.service.client.vo.rechargeLog.RechargeLogVo;
import com.vcc.service.service.*;
import com.vcc.web.core.component.AliComponent;
import com.vcc.web.core.component.OssComponent;
import com.vcc.web.core.component.RedisComponent;
import com.vcc.web.core.util.UserUtil;
import com.vcc.web.core.vo.recharge.CreateOrderVo;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("rechargeLog")
public class RechargeLogController {

    @Autowired
    private RechargeLogService rechargeLogService;
    @Autowired
    private WalletService walletService;
    @Autowired
    private UserService userService;
    @Autowired
    private BossUserService bossUserService;
    @Autowired
    private OssComponent ossComponent;
    @Autowired
    private AliComponent aliComponent;
    @Autowired
    private UserRateService userRateService;
    @Autowired
    private RateService rateService;
    @Autowired
    private RedisComponent redisComponent;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ClientParamService clientParamService;
    @Autowired
    private CurrencyService currencyService;
    @Autowired
    private UsdtRechargeService usdtRechargeService;


    @RestPack
    @ROReject
    @KycReject
    @PostMapping("createOrder")
    @SaCheckPermission("wallet:recharge:create")
    @RepeatSubmit
    @SaCheckLogin
    public CreateOrderVo createOrder(
            @RequestBody @Validated CreateOrderDto createOrderDto
    ) {
        String accountRechargeMinAmount = clientParamService.getValueByCodeAndUserIdOrDefaultValue("ACCOUNT_RECHARGE_MIN_AMOUNT", UserUtil.getId(), "200");
        BigDecimal minAmount = new BigDecimal(accountRechargeMinAmount);
        if (createOrderDto.getRemittanceAmount().compareTo(minAmount) < 0) {
            throw new ProgramException("入金金额不能小于" + minAmount);
        }
        RLock lock = redissonClient.getLock("rechargeLog:createOrder:" + UserUtil.getId());
        BigDecimal remittanceAmount = createOrderDto.getRemittanceAmount();
        String rechargeAddress = clientParamService.getValueByCodeAndUserIdOrDefaultValue("RECHARGE_ADDRESS", UserUtil.getId(), "TX5yofyM8xtqqk7i772ZJxRygrdjw6kLnY");
        try {
            boolean tryLock = lock.tryLock();
            if (!tryLock) {
                throw new ProgramException("请勿重复提交订单");
            }
            if (rechargeLogService.existsByPendingOrder(UserUtil.getId())) {
                throw new ProgramException("您有未处理的订单，请先联系客服处理");
            }
            Wallet wallet = walletService.getById(createOrderDto.getWalletId());
            RechargeLog rechargeLog = new RechargeLog();
            rechargeLog.setUserId(UserUtil.getId());
            rechargeLog.setOrderId(IdWorker.getStringOrderId());
            rechargeLog.setRemark(createOrderDto.getRemark());
            rechargeLog.setUseFlag(createOrderDto.getType());
            rechargeLog.setStatus(2);
            rechargeLog.setCurrencyId(wallet.getCurrencyId());
            rechargeLog.setCurrencyCode(wallet.getCurrencyCode());
            rechargeLog.setRechargeAddress(rechargeAddress);
            rechargeLog.setAmount(BigDecimal.ZERO);
            rechargeLog.setRequestDate(new Date());
            rechargeLog.setRemittanceCurrency(createOrderDto.getRemittanceCurrency());
            rechargeLog.setRemittanceRate(BigDecimal.ONE);
            if (createOrderDto.getType() == 0) {
                synchronized (this) {
                    int index = 0;
                    while (index < 101) {
                        //验证金额是否可以使用  金额需要加上两位随机数
                        BigDecimal bigDecimal = RandomUtil.randomBigDecimal(BigDecimal.ZERO, BigDecimal.ONE);
                        remittanceAmount = remittanceAmount.add(bigDecimal).setScale(2, RoundingMode.HALF_UP);
                        if (!rechargeLogService.verifyAmount(remittanceAmount)) {
                            BigDecimal exchangeRate = wallet.getExchangeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);
                            BigDecimal tempAmount = remittanceAmount.multiply(exchangeRate);
                            BigDecimal amount = remittanceAmount.subtract(tempAmount).setScale(2, RoundingMode.HALF_UP);
                            rechargeLog.setRemittanceAmount(remittanceAmount);
                            rechargeLog.setAmount(amount);
                            index = 1000;
                            rechargeLogService.save(rechargeLog);
                        } else {
                            index++;
                            if (index == 100) {
                                throw new ProgramException("创建订单失败，请稍后再试");
                            }
                        }
                    }
                }
                UsdtRecharge usdtRecharge = getUsdtRecharge(createOrderDto, rechargeLog, rechargeAddress);
                usdtRechargeService.save(usdtRecharge);
            } else {
                BigDecimal exchangeRate = wallet.getExchangeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);
                BigDecimal tempAmount = remittanceAmount.multiply(exchangeRate);
                BigDecimal amount = remittanceAmount.subtract(tempAmount).setScale(2, RoundingMode.HALF_UP);
                rechargeLog.setRemittanceAmount(remittanceAmount);
                rechargeLog.setAmount(amount);
                if (createOrderDto.getType() == 2) {
                    if (StringUtils.isBlank(createOrderDto.getVoucher())) {
                        throw new ProgramException("请填写汇款水单号");
                    } else {
                        if (rechargeLogService.existsByVoucher(createOrderDto.getVoucher())) {
                            throw new ProgramException("该汇款水单号已存在，请重新填写");
                        }
                    }
                }
                rechargeLog.setVoucher(createOrderDto.getVoucher());
                rechargeLogService.save(rechargeLog);
            }
        } catch (Exception e) {
            if (e instanceof ProgramException) {
                throw e;
            }
            throw new ProgramException("创建订单失败，请稍后再试");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return createOrderDto.getType() == 0 ?
                new CreateOrderVo(remittanceAmount, rechargeAddress, QrCodeUtil.generateAsBase64(rechargeAddress, new QrConfig(300, 300), "png"))
                : null;
    }

    private UsdtRecharge getUsdtRecharge(CreateOrderDto createOrderDto, RechargeLog rechargeLog, String rechargeAddress) {
        UsdtRecharge usdtRecharge = new UsdtRecharge();
        usdtRecharge.setUserId(rechargeLog.getUserId());
        usdtRecharge.setOrderId(rechargeLog.getOrderId());
        usdtRecharge.setRemark(createOrderDto.getRemark());
        usdtRecharge.setStatus(2);
        usdtRecharge.setCurrencyId(rechargeLog.getCurrencyId());
        usdtRecharge.setCurrencyCode(rechargeLog.getCurrencyCode());
        usdtRecharge.setConfirmCount(0);
        usdtRecharge.setReceiveAddress(rechargeAddress);
        usdtRecharge.setRemittanceAmount(rechargeLog.getRemittanceAmount());
        usdtRecharge.setRemittanceCurrency(rechargeLog.getRemittanceCurrency());
        usdtRecharge.setNetwork("TRC-20");
        usdtRecharge.setRequestDate(new Date());
        return usdtRecharge;
    }

    @RestPack
    @GetMapping("paging")
    @SaCheckPermission("wallet:recharge:view")
    public Page<RechargeLogVo> paging(
            Page<RechargeLog> pageable,
            Long targetUserId,
            Integer useFlag,
            Integer status,
            String remark,
            String startDate,
            String endDate
    ) {
        User user = UserUtil.get();
        Long parentId = user.getParentId();
        Role role = UserUtil.getRole();
        Page<RechargeLog> paging = rechargeLogService.paging(
                role,
                role.getHasAllData() && parentId != null && parentId != 0 ? parentId : user.getId(),
                pageable,
                targetUserId,
                useFlag,
                status,
                remark,
                startDate,
                endDate);
        List<RechargeLog> records = paging.getRecords();
        if (records == null || records.isEmpty()) {
            return Page.of(
                    paging.getPageNumber(),
                    paging.getPageSize()
            );
        }
        List<Long> userIds = records.stream()
                .map(RechargeLog::getUserId)
                .distinct()
                .toList();
        List<Long> bossUserIds = records.stream().map(RechargeLog::getAuditUserId)
                .distinct()
                .filter(RegexUtil::checkObjectIsNotNull)
                .toList();
        List<User> users = userService.listByIds(userIds);
        List<BossUser> bossUsers;
        if (!bossUserIds.isEmpty()) {
            bossUsers = bossUserService.listByIds(bossUserIds);
        } else {
            bossUsers = null;
        }
        return paging.map(it -> {
            User tempUser = users.stream()
                    .filter(user1 -> user1.getId().equals(it.getUserId()))
                    .findFirst()
                    .orElse(null);
            String auditUser = null;
            if (bossUsers != null) {
                BossUser tempBossUser = bossUsers.stream()
                        .filter(bossUser -> bossUser.getId().equals(it.getAuditUserId()))
                        .findFirst()
                        .orElse(null);
                auditUser = tempBossUser != null ? tempBossUser.getAccount() : null;
            }
            String account = tempUser != null ? tempUser.getAccount() : "";
            String dealerAccount = tempUser != null ? tempUser.getDealerAccount() : "";
            return new RechargeLogVo(it, account, dealerAccount, auditUser);
        });
    }


    @GetMapping("derive")
    @SaCheckPermission(value = "wallet:recharge:derive")
    public void derive(
            Long targetUserId,
            Integer useFlag,
            Integer status,
            String remark,
            String startDate,
            String endDate,
            HttpServletResponse response
    ) {
        User user = UserUtil.get();
        Long parentId = user.getParentId();
        Role role = UserUtil.getRole();
        if (RegexUtil.checkStringIsNull(startDate)) {
            throw new ProgramException("请选择开始时间");
        }
        if (RegexUtil.checkStringIsNull(endDate)) {
            throw new ProgramException("请选择结束时间");
        }
        Long pages = 1L;
        Page<RechargeLog> paging;
        SXSSFWorkbook sxssfWorkbook = FileUtil.createRechargeLogTitle(10000);
        List<Currency> currencyList = currencyService.list();
        do {
            paging = rechargeLogService.paging(
                    role,
                    role.getHasAllData() && parentId != null && parentId != 0 ? parentId : user.getId(),
                    Page.of(pages++, 1000),
                    targetUserId,
                    useFlag,
                    status,
                    remark,
                    startDate,
                    endDate);
            List<RechargeLog> records = paging.getRecords();
            List<Long> userIds = records.stream()
                    .map(RechargeLog::getUserId)
                    .distinct()
                    .toList();
            List<User> users = userService.listByIds(userIds);
            List<RechargeLogFilePojo> list = paging.getRecords().stream().map(it -> {
                User tempUser = users.stream()
                        .filter(user1 -> user1.getId().equals(it.getUserId()))
                        .findFirst()
                        .orElse(null);
                String account = tempUser != null ? tempUser.getAccount() : "";
                Currency currency = currencyList.stream().filter(
                        c -> c.getId().equals(it.getCurrencyId())
                ).findFirst().orElse(null);
                return new RechargeLogFilePojo(
                        it.getOrderId(),
                        DateUtil.format(it.getRequestDate()),
                        account,
                        it.getAmount(),
                        it.getRemittanceAmount(),
                        it.getRemittanceCurrency(),
                        it.getVoucher(),
                        "TRC-20",
                        it.getRemittanceAddress(),
                        it.getRechargeAddress(),
                        it.getRemark(),
                        it.getStatus(),
                        it.getStatus() == 2 ? "" : DateUtil.format(it.getAuditDate()),
                        "",
                        currency != null ? currency.getName() : "");
            }).collect(Collectors.toList());
            FileUtil.deriveRechargeLogData(sxssfWorkbook, list, paging.getPageNumber(), paging.getPageSize());
        } while (!(pages).equals(paging.getTotalPage() + 1));
        String fileName = System.currentTimeMillis() + "入金明细.xlsx";
        FileUtil.downXlsxFile(response, sxssfWorkbook, fileName);
    }

}
